/*******************************************************************************
 ** 
 ** (C) 2011 Kevin Druelle 
 **
 ** this software is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Lesser General Public License as published by
 ** the Free Software Foundation, either version 3 of the License, or
 ** (at your option) any later version.
 ** 
 ** This software is distributed in the hope that it will be useful,
 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ** GNU General Public License for more details.
 ** 
 ** You should have received a copy of the GNU General Public License
 ** along with this software.  If not, see <http://www.gnu.org/licenses/>.
 ** 
 ******************************************************************************/

#ifndef __CPPTOOLBOX_SMART_PTR_H__
#define	__CPPTOOLBOX_SMART_PTR_H__

namespace cpptoolbox{

/**
* A simple counter
*/
class RefCounter {
private:
    unsigned long _counter;
public:

    RefCounter() {
        _counter = 0;
    }

    void addRef() {
        _counter++;
    }

    unsigned long releaseRef() {
        return --_counter;
    }
};

/**
* class SP for Smart Pointer.
* This class provide scoped pointer to object or POD type but not for array
*/
template <class T>
class SP {
private:
    T * _ptr;
    RefCounter * _refCounter;

public:

    SP() {
        _ptr = 0;
        _refCounter = new RefCounter();
        _refCounter->addRef();
    }

    SP(T * pointer) {
        _ptr = pointer;
        _refCounter = new RefCounter();
        _refCounter->addRef();
    }

    SP(const SP<T> & other) {
        _ptr = other._ptr;
        _refCounter = other._refCounter;
        _refCounter->addRef();
    }

    virtual ~SP() {
        if (_refCounter->releaseRef() == 0) {
            release();
        }
    }

    SP<T> & operator=(const SP<T> & other) {
        if (&other != this) {
            if (_refCounter->releaseRef() == 0) {
                release();
            }
            _ptr = other._ptr;
            _refCounter = other._refCounter;
            _refCounter->addRef();
        }
        return *this;
    }

    T * operator ->(){
        return _ptr;
    }

    T & operator*(){
        return *_ptr;
    }
    
    T ** operator &(){
        return &_ptr;
    }
    
protected:
    void release(){
        delete _ptr;
        delete _refCounter;
    }

};

/**
* class SP specialized for Smart Pointer Array.
* This class provide scoped pointer to object or POD type as array
*/
template <class T>
class SP<T[]> {
private:
    T * _ptr;
    RefCounter * _refCounter;

public:

    SP() {
        _ptr = 0;
        _refCounter = new RefCounter();
        _refCounter->addRef();
    }

    SP(T * pointer) {
        _ptr = pointer;
        _refCounter = new RefCounter();
        _refCounter->addRef();
    }

    SP<T[]>(const SP<T[]> & other) {
        _ptr = other._ptr;
        _refCounter = other._refCounter;
        _refCounter->addRef();
    }

    virtual ~SP() {
        if (_refCounter->releaseRef() == 0) {
            release();
        }
    }

    SP<T[]> & operator=(const SP<T[]> & other) {
        if (&other != this) {
            if (_refCounter->releaseRef() == 0) {
                release();
            }
            _ptr = other._ptr;
            _refCounter = other._refCounter;
            _refCounter->addRef();
        }
        return *this;
    }

    T * operator ->(){
        return _ptr;
    }

    T & operator*(){
        return *_ptr;
    }
    
    T ** operator &(){
        return &_ptr;
    }
	
	T & operator [](unsigned long i){
        return _ptr[i];
    }
    
protected:
    void release(){
        delete _ptr;
        delete _refCounter;
    }

};

} // end of cpptoolbox namspace

#endif	/* __CPPTOOLBOX_SMART_PTR_H__ */
